#include "stdafx.h"
#include "game_level.h"
#include "object_destroy_functional.h"
#include <algorithm>

namespace rpg {

	GameLevel::GameLevel(void) {
		mActiveCamera = NULL;
	}

	GameLevel::GameLevel(const Ogre::String &name) : mName(name) {
		mActiveCamera = NULL;
	}

	GameLevel::~GameLevel(void) {
		std::for_each(mActorList.begin(), mActorList.end(), ObjectDestroyFunctional<Actor>());
		std::for_each(mCameraList.begin(), mCameraList.end(), ObjectDestroyFunctional<GameCamera>());
	}

	Ogre::String GameLevel::getName(void) const {
		return mName;
	}

	void GameLevel::setName(const Ogre::String &name) {
		mName = name;
	}

	bool GameLevel::isActor(const Ogre::String &name) const {
		return (mActorSearchMap.find(name) != mActorSearchMap.end());
	}

	Actor * GameLevel::getActorByName(const Ogre::String &actorName) {
		Actor *result = NULL;
		std::map<Ogre::String, Actor *>::iterator map_idx;

		map_idx = mActorSearchMap.find(actorName);
		if(map_idx != mActorSearchMap.end()) {
			result = map_idx->second;
		}

		return result;
	}

	Actor * GameLevel::addNewActor(const ActorDesc &actorDesc) {
		Actor *newActor = NULL;

		if(!isActor(actorDesc.levelObjectDesc.name)) {
			newActor = new Actor(actorDesc);

			mActorList.push_back(newActor);
			mActorSearchMap.insert(std::make_pair(actorDesc.levelObjectDesc.name, newActor));
		}

		return newActor;
	}

	void GameLevel::removeActor(const Ogre::String &actorName) {
		std::vector<Actor *>::iterator list_idx;
		Actor *actorObject;

		if(isActor(actorName)) {
			mActorSearchMap.erase(actorName);
			list_idx = std::find_if(mActorList.begin(), mActorList.end(), FindObjectByName<Actor>(actorName));
			actorObject = *list_idx;
			mActorList.erase(list_idx);
			delete actorObject;
		}
	}

	bool GameLevel::isCamera(const Ogre::String &cameraName) const {
		return (mCameraSearchMap.find(cameraName) != mCameraSearchMap.end());
	}

	GameCamera * GameLevel::getCameraByName(const Ogre::String &cameraName) {
		GameCamera *result = NULL;
		std::map<Ogre::String, GameCamera *>::iterator map_idx;

		map_idx = mCameraSearchMap.find(cameraName);
		if(map_idx != mCameraSearchMap.end()) {
			result = map_idx->second;
		}

		return result;
	}

	GameCamera * GameLevel::addNewCamera(const GameCameraDesc &gameCameraDesc) {
		GameCamera *newCamera = NULL;

		if(!isActor(gameCameraDesc.levelObjectDesc.name)) {
			newCamera = new GameCamera(gameCameraDesc);

			mCameraList.push_back(newCamera);
			mCameraSearchMap.insert(std::make_pair(gameCameraDesc.levelObjectDesc.name, newCamera));
		}

		return newCamera;
	}

	void GameLevel::removeCamera(const Ogre::String &cameraName) {
		std::vector<GameCamera *>::iterator list_idx;
		GameCamera *cameraObject;

		if(isCamera(cameraName)) {
			mCameraSearchMap.erase(cameraName);
			list_idx = std::find_if(mCameraList.begin(), mCameraList.end(), FindObjectByName<GameCamera>(cameraName));
			cameraObject = *list_idx;
			mCameraList.erase(list_idx);

			if(cameraObject == mActiveCamera) {
				mActiveCamera = NULL;
			}

			delete cameraObject;
		}
	}

	GameCamera * GameLevel::getActiveCamera(void) {
		return mActiveCamera;
	}

	void GameLevel::setActiveCamera(const Ogre::String &cameraName) {
		GameCamera *result = NULL;
		std::map<Ogre::String, GameCamera *>::iterator map_idx;

		map_idx = mCameraSearchMap.find(cameraName);
		if(map_idx != mCameraSearchMap.end()) {
			mActiveCamera = map_idx->second;
		}
	}

	void GameLevel::buildWaypoint(const Ogre::Vector3 &startPoint, const Ogre::Vector3 &endPoint, std::list<Ogre::Vector3> *waypoint) const {
		Ogre::Vector3 moveDirections[] = {Ogre::Vector3::UNIT_X * 5.0f,
										Ogre::Vector3::UNIT_X * 2.5f + Ogre::Vector3::UNIT_Z * 5.0f,
										Ogre::Vector3::NEGATIVE_UNIT_X * 2.5f + Ogre::Vector3::UNIT_Z * 5.0f,
										Ogre::Vector3::NEGATIVE_UNIT_X * 5.0f,
										Ogre::Vector3::NEGATIVE_UNIT_X * 2.5f + Ogre::Vector3::NEGATIVE_UNIT_Z * 5.0f,
										Ogre::Vector3::UNIT_X * 2.5f + Ogre::Vector3::NEGATIVE_UNIT_Z * 5.0f};

		Ogre::Vector3 currentPoint = startPoint;
		Ogre::Vector3 moveVector = endPoint - currentPoint;
		Ogre::Vector3 currentDirection;

		if(waypoint != NULL) {
			waypoint->clear();

			while(moveVector.length() > 4.0f) {
				waypoint->push_back(currentPoint);

				currentDirection = *std::min_element(moveDirections, moveDirections + 6, VectorAnglesComparator(moveVector));

				currentPoint += currentDirection;
				moveVector = endPoint - currentPoint;
			}

			waypoint->push_back(currentPoint);
		}
	}

	void GameLevel::update(float deltaTime) {
		std::for_each(mActorList.begin(), mActorList.end(), LevelObjectUpdater<Actor>(deltaTime));
		std::for_each(mCameraList.begin(), mCameraList.end(), LevelObjectUpdater<GameCamera>(deltaTime));
	}

	GameLevel::VectorAnglesComparator::VectorAnglesComparator(const Ogre::Vector3 &customVector) : mCustomVector(customVector) {
	}

	bool GameLevel::VectorAnglesComparator::operator () (const Ogre::Vector3 &v1, const Ogre::Vector3 &v2) const {
		Ogre::Vector3 v1Copy(v1);
		Ogre::Vector3 v2Copy(v2);

		return (Ogre::Math::Abs(v1Copy.angleBetween(mCustomVector)) < Ogre::Math::Abs(v2Copy.angleBetween(mCustomVector)));
	}

}